package com.tiancheng.trade.merchant.channel.handler.ums;

import cn.hutool.core.codec.Base64Encoder;
import com.dtflys.forest.Forest;
import com.dtflys.forest.http.ForestRequest;
import com.fasterxml.jackson.core.type.TypeReference;
import com.tiancheng.trade.commom.core.data.ApplicationInfo;
import com.tiancheng.trade.commom.core.enums.MerchantRegStatusEnums;
import com.tiancheng.trade.commom.core.enums.PaymentChannelEnum;
import com.tiancheng.trade.commom.core.exception.Asserts;
import com.tiancheng.trade.commom.core.exception.error.BusinessErrorInfoEnum;
import com.tiancheng.trade.commom.core.utils.JsonUtil;
import com.tiancheng.trade.commom.web.model.request.merchant.MerchantBranchBankListReqVO;
import com.tiancheng.trade.commom.web.model.response.MerchantBranchBankListResVO;
import com.tiancheng.trade.merchant.channel.AbstractMerchantRegChannel;
import com.tiancheng.trade.merchant.channel.bo.MerchantRegSmsSendBO;
import com.tiancheng.trade.merchant.channel.bo.MerchantRegSmsVerifyBO;
import com.tiancheng.trade.merchant.channel.handler.ums.request.*;
import com.tiancheng.trade.merchant.channel.handler.ums.response.*;
import com.tiancheng.trade.merchant.convert.MerchantConvert;
import com.tiancheng.trade.merchant.dto.message.ChannelNotifyMessage;
import com.tiancheng.trade.merchant.dto.requestdto.MerchantPayRegVO;
import com.tiancheng.trade.merchant.dto.requestdto.MinioUploadDto;
import com.tiancheng.trade.merchant.dto.responsedto.MerchantRegStatusQueryResDTO;
import com.tiancheng.trade.merchant.enums.MerchantApplyStatusEnums;
import com.tiancheng.trade.merchant.enums.MerchantApplySubStatusEnums;
import com.tiancheng.trade.merchant.enums.MerchantTypeEnums;
import com.tiancheng.trade.merchant.model.MerchantPayReg;
import com.tiancheng.trade.merchant.model.MerchantPayRegFile;
import com.tiancheng.trade.merchant.util.DESUtil;
import com.tiancheng.trade.merchant.util.SHAUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.collections4.MapUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.security.NoSuchAlgorithmException;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import static com.tiancheng.trade.merchant.util.UmsUtil.*;

/**
 * @Author: kellen
 * @Description: 银联支付渠道
 * @Date: create in 2024/11/19 18:33
 */
@Slf4j
@Service
public class UmsMerchantRegChannel implements AbstractMerchantRegChannel {

    private static final String HOST = "https://selfapply-test.chinaums.com/self-contract-nmrs/interface/autoReg";
    public static final int FILE_MAX_SIZE = 2 * 1024 * 1024;
    public static final String ACCESSER_ID = "89853057399799G";
    public static final String SIGN_TYPE = "SHA-256";
    // 签名密钥
    private static final String SIGN_KEY = "udik876ehjde32dU61edsxsf";

    @Override
    public MerchantPayRegFile upload(MultipartFile file, Integer regMode, MinioUploadDto upload) {
        final long size = file.getSize();
        BusinessErrorInfoEnum.MERCHANT_REG_ID_PHOTO_SIZE_ERROR.assertIsTrue(size < FILE_MAX_SIZE);

        UmsMerchantRegIdFileUploadRequest uploadRequest = new UmsMerchantRegIdFileUploadRequest();
        uploadRequest.setService("pic_upload");
        uploadRequest.setAccesser_id(ACCESSER_ID);
        uploadRequest.setSign_type(SIGN_TYPE);
        uploadRequest.setRequest_date(LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHmmss")));
        uploadRequest.setRequest_seq(requestSeqHelper());
        uploadRequest.setPic_base64(generateBase64(file));
        UmsMerchantRegIdFileUploadResponse response = (UmsMerchantRegIdFileUploadResponse) doPost(uploadRequest, UmsMerchantRegIdFileUploadResponse.class);
        BusinessErrorInfoEnum.MERCHANT_REG_ID_PHOTO_UNKNOWN_ERROR.assertIsTrue(response.isOk());

        /*final UmsMerchantRegIdFileUploadResponse response = new UmsMerchantRegIdFileUploadResponse();
        response.setFile_path("http://127.0.0.1:8205/sdhjsdf.png");
        response.setFile_type("png");
        response.setFile_size(String.valueOf(size));
        response.setAccesser_reg_id(uploadRequest.getRequest_seq());*/

        MerchantPayRegFile merchantPayRegFile = new MerchantPayRegFile();
        merchantPayRegFile.setFilePath(response.getFile_path());
        merchantPayRegFile.setFileType(response.getFile_type());
        merchantPayRegFile.setFileSize(response.getFile_size());
        merchantPayRegFile.setChannelRegId(response.getAccesser_reg_id());
        return merchantPayRegFile;
    }

    @Override
    public MerchantPayReg complexUpload(ApplicationInfo application, MerchantPayRegVO merchantPayRegVO) {

        UmsMerchantRegComplexRequest params = new UmsMerchantRegComplexRequest();
        BeanUtils.copyProperties(merchantPayRegVO, params);
        if(StringUtils.isNotEmpty(merchantPayRegVO.getMccCode())){
            params.setMccCode(Integer.parseInt(merchantPayRegVO.getMccCode()));
        }
        params.setService("complex_upload");
        params.setAccesser_id(ACCESSER_ID);
        params.setSign_type(SIGN_TYPE);
        params.setRequest_date(LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHmmss")));
        params.setRequest_seq(requestSeqHelper());

        /*final UmsMerchantRegComplexResponse response = doPost(params, UmsMerchantRegComplexResponse.class);
        log.info("提交详细资料接口返回：{}", JsonUtil.toJsonString(response));
        Asserts.isTrue(response.isOk(), StringUtils.isNotEmpty(response.getRes_msg()) ? response.getRes_msg() : "提交银商商户进件材料异常！");
        final MerchantPayReg merchantPayReg = MerchantConvert.INSTANCE.convert2MerchantPayReg(merchantPayRegVO);
        merchantPayReg.setApplyStatus(0);
        merchantPayReg.setIsChain(1);
        merchantPayReg.setChannelRegId(response.getUms_reg_id());*/

        final MerchantPayReg merchantPayReg = MerchantConvert.INSTANCE.convert2MerchantPayReg(merchantPayRegVO);
        merchantPayReg.setApplyStatus(0);
        merchantPayReg.setIsChain(1);
        merchantPayReg.setChannelRegId(params.getRequest_seq());
        return merchantPayReg;
    }

    @Override
    public List<MerchantBranchBankListResVO> branchBankList(MerchantBranchBankListReqVO merchantBranchBankListReqVO) {
        final UmsMerchantReqQueryBankListRequest params = JsonUtil.fromJson(merchantBranchBankListReqVO, new TypeReference<UmsMerchantReqQueryBankListRequest>() {
        });
        params.setService("branch_bank_list");
        params.setAccesser_id(ACCESSER_ID);
        params.setSign_type(SIGN_TYPE);
        params.setRequest_date(LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHmmss")));
        params.setRequest_seq(requestSeqHelper());
        final UmsMerchantRegQueryBankListResponse response = doPost(params, UmsMerchantRegQueryBankListResponse.class);
        log.info("查询支行列表接口返回：{}", response);
        Asserts.isTrue(response.isOk(), "查询支行列表异常！");
        if (CollectionUtils.isNotEmpty(response.getBranchBankList())) {
            List<MerchantBranchBankListResVO> result = new ArrayList<>();
            for (UmsMerchantRegQueryBankListResponse.BankInfo bankInfo : response.getBranchBankList()) {
                final MerchantBranchBankListResVO b = new MerchantBranchBankListResVO();
                b.setCode(bankInfo.getCode());
                b.setBankBranchName(bankInfo.getBankBranchName());
                result.add(b);
            }
            return result;
        }
        return null;
    }

    @Override
    public String queryRegin(String dataType) {
        final UmsMerchantReqQueryReginRequest params = new UmsMerchantReqQueryReginRequest();
        params.setService("data_download");
        params.setAccesser_id(ACCESSER_ID);
        params.setSign_type(SIGN_TYPE);
        params.setRequest_date(LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHmmss")));
        params.setRequest_seq(requestSeqHelper());
        params.setData_type(dataType);
        return generateGetUrl(params);
    }

    @Override
    public void requestAccountVerify(UmsMerchantReqRequestAccountVerifyRequest params) {
        params.setService("request_account_verify");
        params.setAccesser_id(ACCESSER_ID);
        params.setSign_type(SIGN_TYPE);
        params.setRequest_date(LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHmmss")));
        params.setRequest_seq(requestSeqHelper());
        final UmsMerchantRegBaseResponse response = doPost(params, UmsMerchantRegBaseResponse.class);
        log.info("调用对账账户打款验证接口返回：{}", response);
        BusinessErrorInfoEnum.MERCHANT_REG_ERROR.assertIsTrue(response.isOk(), StringUtils.isNotEmpty(response.getRes_msg())?response.getRes_msg():"对公账户验证异常");

    }

    @Override
    public void companyAccountVerify(UmsMerchantReqCompanyAccountVerifyRequest params) {
        params.setService("company_account_verify");
        params.setAccesser_id(ACCESSER_ID);
        params.setSign_type(SIGN_TYPE);
        params.setRequest_date(LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHmmss")));
        params.setRequest_seq(requestSeqHelper());
        final UmsMerchantRegBaseResponse response = doPost(params, UmsMerchantRegBaseResponse.class);
        log.info("调用对公账户认证接口返回：{}", response);
        BusinessErrorInfoEnum.MERCHANT_REG_ERROR.assertIsTrue(response.isOk(), StringUtils.isNotEmpty(response.getRes_msg())?response.getRes_msg():"对公账户打款金额验证异常！");
    }

    @Override
    public void smsSend(MerchantRegSmsSendBO merchantRegSmsSendBO) {

    }

    @Override
    public void smsVerify(MerchantRegSmsVerifyBO merchantRegSmsVerifyBO) {

    }

    /**
     * 商户进件状态查询
     *
     * @param params
     * @return
     */
    @Override
    public MerchantRegStatusQueryResDTO queryRegisterStatus(UmsMerchantReqStatusQueryRequest params) {
        Asserts.isNotEmpty(params.getUms_reg_id(), "进件流水号不允许为空！");
        params.setService("apply_qry");
        params.setAccesser_id(ACCESSER_ID);
        params.setSign_type(SIGN_TYPE);
        params.setRequest_date(LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHmmss")));
        params.setRequest_seq(requestSeqHelper());
        final UmsMerchantRegStatusQueryRes response = doPost(params, UmsMerchantRegStatusQueryRes.class);
        log.info("调用银联查询商户进件状态接口返回：{}", response);
        BusinessErrorInfoEnum.MERCHANT_REG_ERROR.assertIsTrue(response.isOk() && StringUtils.isNotEmpty(response.getApply_status()), "查询商户进件状态接口异常！");
        MerchantRegStatusQueryResDTO res = new MerchantRegStatusQueryResDTO();
        res.setFlowId(params.getUms_reg_id());
        String applyStatus = response.getApply_status();
        res.setApplySubStatus(applyStatus);
        res.setSourceApplyStatus(applyStatus);
        switch (applyStatus) {
            case "00", "01", "02","34" -> {
                // 签约中
                res.setApplyStatus(MerchantApplyStatusEnums.ing.getCode());
                res.setApplySubStatus(MerchantApplySubStatusEnums.ing.getCode());
            }
            case "03" -> {
                // 入网成功
                res.setApplyStatus(MerchantApplyStatusEnums.success.getCode());
                res.setApplySubStatus(MerchantApplySubStatusEnums.success.getCode());
            }
            case "04" -> {
                // 入网失败，最终失败
                res.setApplyStatus(MerchantApplyStatusEnums.final_error.getCode());
                res.setApplySubStatus(MerchantApplySubStatusEnums.final_error.getCode());
            }
            default -> {
                // 其他失败
                res.setApplyStatus(MerchantApplyStatusEnums.exception.getCode());
                switch (applyStatus) {
                    case "05" -> res.setApplySubStatus(MerchantApplySubStatusEnums.dgzh_yc.getCode());
                    case "28" -> res.setApplySubStatus(MerchantApplySubStatusEnums.zlyz_yc.getCode());
                    default -> res.setApplySubStatus(MerchantApplySubStatusEnums.other.getCode());
                }
            }
        }
        return res;
    }

    @Override
    public MerchantRegStatusEnums nextFlow(MerchantRegStatusEnums currentFlow, MerchantTypeEnums merchantType) {
        Asserts.isTrue(null != currentFlow && null != merchantType, "参数异常！");
        if(merchantType.equals(MerchantTypeEnums.xiao_wei)){
            // 小微商户
            switch (currentFlow){
                case INIT:
                    return MerchantRegStatusEnums.SIGNING_IN_PROGRESS;
                case CERTIFICATE_VERIFICATION_FAILED:
                    return MerchantRegStatusEnums.INIT;
                case CERTIFICATE_VERIFICATION_SUCCESS:
                    return MerchantRegStatusEnums.SIGNING_IN_PROGRESS;
                case SIGNING_IN_PROGRESS:
                    return MerchantRegStatusEnums.PROCESS_COMPLETED;
            }
        }else if(merchantType.equals(MerchantTypeEnums.qi_ye) ||merchantType.equals(MerchantTypeEnums.ge_ren)){
            // 企业商户
            // 个体工商
            switch (currentFlow){
                case INIT:
                    return MerchantRegStatusEnums.CORPORATE_ACCOUNT_VERIFICATION;
                case CORPORATE_ACCOUNT_VERIFICATION:
                    return MerchantRegStatusEnums.CORPORATE_ACCOUNT_VERIFICATION_ING;
                case CORPORATE_ACCOUNT_VERIFICATION_ING:
                    return MerchantRegStatusEnums.SIGNING_IN_PROGRESS;
                case CORPORATE_ACCOUNT_VERIFICATION_SUCCESS:
                    return MerchantRegStatusEnums.SIGNING_IN_PROGRESS;
                case SIGNING_IN_PROGRESS:
                    return MerchantRegStatusEnums.PROCESS_COMPLETED;
            }
        }
        return null;
    }

    @Override
    public boolean needSign() {
        return true;
    }

    @Override
    public ChannelNotifyMessage callbackMsg(Map<String, Object> paramsMap) {
        ChannelNotifyMessage channelNotifyMessage = new ChannelNotifyMessage();
        String signData = MapUtils.getString(paramsMap, "sign_data");
        String jsonData = MapUtils.getString(paramsMap, "json_data");
        if(StringUtils.isEmpty(signData) || StringUtils.isEmpty(jsonData)){
            log.error("银联回调商户消息体为空！");
            return null;
        }
        channelNotifyMessage.setAccesserId(MapUtils.getString(paramsMap, "accesser_id"));
        channelNotifyMessage.setChannelCode(PaymentChannelEnum.UMS.getCode());
        try {
            String decrypt = decrypt(jsonData, SIGN_KEY);
            Map<String,Object> map = JsonUtil.fromJson(decrypt, Map.class);
            channelNotifyMessage.setMessageBody(map);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        return channelNotifyMessage;
    }


    @Override
    public String agreementSign(UmsMerchantReqAgreementSignRequest params) {
        params.setService("agreement_sign");
        params.setAccesser_id(ACCESSER_ID);
        params.setSign_type(SIGN_TYPE);
        params.setRequest_date(LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHmmss")));
        params.setRequest_seq(requestSeqHelper());
        return generateGetUrl(params);
    }

    private <T extends UmsMerchantRegBaseResponse> T doPost(Object params, Class<T> responseC) {
        Map<String, String> body = new HashMap<>();
        // 2、待签名的字符串
        String reqJson = JsonUtil.toJsonString(params);
        // 3、SHA-256签名
        try {
            // 3、SHA-256签名
            String signedStr = SHAUtil.sign(reqJson);

            // 4、3DES加密
            String desStr = DESUtil.encrypt(reqJson, SIGN_KEY);
            body.put("json_data", desStr);
            body.put("sign_data", signedStr);
            body.put("accesser_id", ACCESSER_ID);
            ForestRequest<?> request = Forest.post(HOST).addBody(body);
            request.setConnectTimeout(5 * 1000);// 5s
            request.setReadTimeout(100 * 1000);
            request.addHeader("content-type", "application/x-www-form-urlencoded;charset=utf-8");
            return request.execute(responseC);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }


    /**
     * 将MultipartFile 图片文件编码为base64
     *
     * @param file
     * @return
     * @throws Exception
     */
    private String generateBase64(MultipartFile file) {
        if (file == null || file.isEmpty()) {
            throw new RuntimeException("图片不能为空！");
        }
        String fileName = file.getOriginalFilename();
        String fileType = fileName.substring(fileName.lastIndexOf("."));
        String contentType = file.getContentType();
        byte[] imageBytes = null;
        String base64EncoderImg = "";
        try {
            imageBytes = file.getBytes();
            Base64Encoder base64Encoder = new Base64Encoder();
            /**
             * 1.Java使用BASE64Encoder 需要添加图片头（"data:" + contentType + ";base64,"），
             *   其中contentType是文件的内容格式。
             * 2.Java中在使用BASE64Enconder().encode()会出现字符串换行问题，这是因为RFC 822中规定，
             *   每72个字符中加一个换行符号，这样会造成在使用base64字符串时出现问题，
             *   所以我们在使用时要先用replaceAll("[\\s*\t\n\r]", "")解决换行的问题。
             */
            base64EncoderImg = "data:" + contentType + ";base64," + base64Encoder.encode(imageBytes);
            base64EncoderImg = base64EncoderImg.replaceAll("[\\s*\t\n\r]", "");
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        return base64EncoderImg;
    }

    private String generateGetUrl(UmsMerchantRegBaseRequest params) {
        try {
            // 2、待签名的字符串
            String reqJson = JsonUtil.toJsonString(params);
            // 3、SHA-256签名
            String signedStr = SHAUtil.sign(reqJson);

            // 4、3DES加密
            String desStr = DESUtil.encrypt(reqJson, SIGN_KEY);

            return HOST + "?sign_data=" + signedStr + "&json_data=" + desStr + "&accesser_id=" + ACCESSER_ID;
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (NoSuchAlgorithmException e) {
            throw new RuntimeException(e);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        return null;
    }

}
